package cn.heapstack.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 * @author ehaojii
 */
public class JOptionParser
{
	/**
	 * The hashmap to save Option, the key is long option string
	 * @see {@link Option}
	 */
    HashMap<String, Option> optionMap = new HashMap<String, Option>();
    /**
     * This variable was used to remember the order when you add option
     * so that you can get a beautified help message output
     */
    ArrayList<String> optionArrayInOrder = new ArrayList<String>();
    
    public void addOption(Option option)
    {
        optionArrayInOrder.add(option.getLongOptStr());
        optionMap.put(option.getLongOptStr(), option);
    }
    
    public Option getOptionByLongOptStr(String longOptStr)
    {
        return optionMap.get(longOptStr);
    }
    
    public Option getOptionByShortOptStr(String shortOptStr)
    {
        Iterator<String> keys = optionMap.keySet().iterator();
        while (keys.hasNext())
        {
            String key = keys.next();
            Option o = optionMap.get(key);
            if (o.getShortOptStr().compareTo(shortOptStr) == 0)
            {
                return o;
            }
        }
        return null;
    }
    public void printUsage(String useage)
    {
        StringBuilder sb = new StringBuilder("Usage : ");
        sb.append(useage);
        
        for (int i = 0; i < optionArrayInOrder.size(); i++)
        {
            Option theOne = optionMap.get(optionArrayInOrder.get(i));
            sb.append("\n  ");
            sb.append("-");
            sb.append(theOne.getShortOptStr());
            sb.append(",-");
            sb.append(theOne.getLongOptStr());
            sb.append(" ");
            if (theOne.isRequired())
            {
                sb.append("\t[option required]");
            }
            
            if (theOne.isMustSpecifyValue())
            {
                sb.append("\t[value required]");
            }

            sb.append("\t");
            if (theOne.getShortOptStr().compareToIgnoreCase("H") != 0)
            {
                sb.append(theOne.getHelpMessage());
            }
        }
        
        System.out.println(sb.toString());
    }
    
    public void printOptionsInfo()
    {
        Iterator<String> keys = optionMap.keySet().iterator();
        while (keys.hasNext())
        {
            String key = keys.next();
            Option theOne = optionMap.get(key);
            System.out.println(theOne.toString());
        }
    }
    
    public void parse(String[] args)
    {
        boolean needHelp = false;
        
        Option o = null;
        for (String string : args) {
            if (string.startsWith("-"))
            {
                String key = string.substring(1);
                if (key.compareToIgnoreCase("h") == 0 || key.compareToIgnoreCase("help") == 0)
                {
                    needHelp = true;
                }
                
                o = this.getOptionByLongOptStr(key);
                if (o == null)
                {
                    o = this.getOptionByShortOptStr(key);
                }
                
                if (o != null)
                {
                    o.setSpecified(true);
                }
            }
            else if (o != null)
            {
                o.setOptValue(string);
                o = null;
            }
        }
        

        
        if (needHelp)
        {
            // If set -H option, here will print usage message
            printUsage(getOptionByLongOptStr("help").getHelpMessage());
            System.exit(0);
        }
        else
        {
            Iterator<String> keys2 = optionMap.keySet().iterator();
            while (keys2.hasNext())
            {
                String key = keys2.next();
                Option theOne = optionMap.get(key);
                
                validateOption(theOne);
            }
        }
    }
    
    public void validateOption(Option option) throws IllegalArgumentException
    {
        if (option.isRequired() || option.isSpecified())
        {
            if (option.isMustSpecifyValue() && option.getOptValue() == null)
            {
                String message = "Validation failed" + " \n\tdetails: Option -" + option.getShortOptStr() 
                    + " value has not been specified\n\tplease check: " + option;
                IllegalArgumentException e = new IllegalArgumentException(message);
                throw e;
            }
        }
        
    }
}
